﻿#include <iostream>
#include <ctime>
#include <CL/opencl.h>

char* readFileAsText(const char* path);

static char buffer[256];

int main(int argc, char *argv[])
{
	cl_int err;

	cl_platform_id platform[20];
	cl_device_id device[200];

	unsigned device_p = 0;
	cl_uint size_platform;
	cl_uint size_device;

	err = clGetPlatformIDs(20, platform, &size_platform);
	if (err != CL_SUCCESS) {
		printf("ERROR: Cannot get platform");
		return 1;
	}
	printf("Got %d Platform:\n", size_platform);
	for (unsigned i = 0; i < size_platform; i++) {
		clGetPlatformInfo(platform[i], CL_PLATFORM_NAME, 255, buffer, NULL);
		printf("    %2d: %s\n", i, buffer);

		clGetDeviceIDs(platform[i], CL_DEVICE_TYPE_ALL, 200 - device_p, &device[device_p], &size_device);
		for (unsigned j = 0; j < size_device; j++) {
			clGetDeviceInfo(device[device_p + j], CL_DEVICE_NAME, 255, buffer, NULL);
			printf("        Device %u.%u: %s\n", i, j, buffer);
		}
		device_p += size_device;
	}
	printf("\nFind %d devices at all.\n\n", device_p);

	cl_context context = clCreateContext(NULL, 1, device, NULL, NULL, &err);
	if (err != CL_SUCCESS) {
		printf("ERROR: Create context failed\n");
		return 3;
	}
	puts("Got Context");

	cl_command_queue queue = clCreateCommandQueue(context, device[0], 0, &err);
	if (err != CL_SUCCESS) {
		printf("ERROR: Create command queue failed\n");
		return 4;
	}
	puts("Created Queue");

	const unsigned LENGTH = 1048576;
	const double DX = 1.0 / (double)LENGTH;
	double* hA = new double[LENGTH];
	double* hB = new double[LENGTH];
	if (hA == NULL || hB == NULL) return -1;

	unsigned i;
	double x;
	for (i = 0, x = 0; i < LENGTH; i++, x += DX) {
		hA[i] = x;
		hB[i] = 0.0;
	}
	puts("Host Memory Loaded");

	cl_mem mA = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(double) * LENGTH, hA, NULL);
	cl_mem mB = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double) * LENGTH, NULL, NULL);
	if (mA == NULL || mB == NULL) {
		printf("ERROR: Create memory buffer failed\n");
		return 5;
	}
	puts("Created Buffer");

	const char* source = readFileAsText(argv[1]);
	if (source == NULL) {
		printf("ERROR: Read CL failed\n");
		return 6;
	}
	puts("Read CL program source");

	cl_program program = clCreateProgramWithSource(context, 1, &source, NULL, NULL);
	if (program == NULL) {
		printf("ERROR: Create program failed\n");
		return 7;
	}
	puts("Created CL program from source");

	err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("ERROR: Build program failed\n");
		return 8;
	}
	puts("Built program");

	cl_kernel kernel = clCreateKernel(program, "calculate", NULL);
	if (kernel == NULL) {
		printf("ERROR: Create kernel failed\n");
		return 9;
	}

	err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &mA);
	err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &mB);
	if (err != CL_SUCCESS) {
		printf("ERROR: Set kernel arg failed\n");
		return 10;
	}

	size_t global_size[] = { LENGTH };
	size_t local_size[] = { LENGTH / 1024 };
	err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, global_size, local_size, 0, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("ERROR: Run kernel failed\n");
		return 11;
	}

	time_t t;
	time(&t);
	puts(ctime(&t));
	puts("Running ...");

	err = clEnqueueReadBuffer(queue, mB, CL_TRUE, 0, sizeof(double) * LENGTH, hB, 0, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("ERROR: Read data failed\n");
		return 12;
	}
	puts("Finish\n");

	double PI = 0.0;
	for (i = 0; i < LENGTH; i++) {
		PI += hB[i];
	}
	PI = PI * 4;
	printf("Result: PI = %.14f\n", PI);

	time(&t);
	puts(ctime(&t));

	return 0;
}

char* readFileAsText(const char* path) {
	FILE* file = fopen(path, "r");
	if (file == NULL) return NULL;
	fseek(file, 0, SEEK_END);
	size_t flen = ftell(file);
	fseek(file, 0, SEEK_SET);
	char* R = (char*)malloc(flen + 1);
	if (R == NULL) {
		fclose(file);
		return NULL;
	}
	char* p = R;
	int ch;
	while ((ch = fgetc(file)) != EOF) {
		*p = ch;
		p++;
	}
	*p = '\0';
	fclose(file);
	return R;
}
